#include "TextureShader.h"
#include "Device.h"

using namespace Graphics;
RTTI_DEFINITIONS(TextureShader)

#ifdef DX_11
TextureShader::TextureShader(void)
	:Shader("TextureShader","")
{
}

TextureShader::~TextureShader(void)
{
	Deinitialize();
}

bool TextureShader::Initialize()
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	bool result = InitializeShader(device->GetWindowsSettings().WindowHandle, 
								   "..\\..\\assets\\shaders\\texture.vs", 
								   "..\\..\\assets\\shaders\\texture.ps");
	return result;
}

void TextureShader::Deinitialize()
{
	DeinitializeShader();
}

bool TextureShader::Render(int indexCount, int instanceCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, Texture* texture)
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	
	bool result = SetShaderParameters(worldMatrix, viewMatrix, projectionMatrix, texture);

	result = RenderShader(device, indexCount, instanceCount);

	return result;
}

bool TextureShader::Render(int indexCount, int instanceCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture)
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	
	bool result = SetShaderParameters(worldMatrix, viewMatrix, projectionMatrix, texture);

	result = RenderShader(device, indexCount, instanceCount);

	return result;
}


bool TextureShader::InitializeShader(HWND hwnd, const char* vsFilename,  const char* psFilename)
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	if(Shader::InitializeShader(device, hwnd, vsFilename, psFilename))
	{
		// Now setup the layout of the data that goes into the shader.
		// This setup needs to match the VertexType stucture in the ModelClass and in the shader.
		D3D11_INPUT_ELEMENT_DESC polygonLayout[4];
		polygonLayout[0].SemanticName = "POSITION";
		polygonLayout[0].SemanticIndex = 0;
		polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
		polygonLayout[0].InputSlot = 0;
		polygonLayout[0].AlignedByteOffset = 0;
		polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		polygonLayout[0].InstanceDataStepRate = 0;

		polygonLayout[1].SemanticName = "COLOR";
		polygonLayout[1].SemanticIndex = 0;
		polygonLayout[1].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
		polygonLayout[1].InputSlot = 0;
		polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
		polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		polygonLayout[1].InstanceDataStepRate = 0;

		polygonLayout[2].SemanticName = "TEXCOORD";
		polygonLayout[2].SemanticIndex = 0;
		polygonLayout[2].Format = DXGI_FORMAT_R32G32_FLOAT;
		polygonLayout[2].InputSlot = 0;
		polygonLayout[2].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
		polygonLayout[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		polygonLayout[2].InstanceDataStepRate = 0;

		polygonLayout[3].SemanticName = "TEXCOORD";
		polygonLayout[3].SemanticIndex = 1;
		polygonLayout[3].Format = DXGI_FORMAT_R32G32B32_FLOAT;
		polygonLayout[3].InputSlot = 1;
		polygonLayout[3].AlignedByteOffset = 0;
		polygonLayout[3].InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA;
		polygonLayout[3].InstanceDataStepRate = 1;

		// Get a count of the elements in the layout.
		unsigned int  numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

		// Create the vertex input layout.
		HRESULT result = device->GetDevice()->CreateInputLayout(polygonLayout, numElements, mVertexShaderBuffer->GetBufferPointer(), mVertexShaderBuffer->GetBufferSize(), &mLayout);
		CHECK(result);

		// Release the vertex shader buffer and pixel shader buffer since they are no longer needed.
		mVertexShaderBuffer->Release();
		mVertexShaderBuffer = NULL;

		mPixelShaderBuffer->Release();
		mPixelShaderBuffer = NULL;

		// Create a texture sampler state description.
		D3D11_SAMPLER_DESC samplerDesc;
		samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
		samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
		samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
		samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
		samplerDesc.MipLODBias = 0.0f;
		samplerDesc.MaxAnisotropy = 1;
		samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
		samplerDesc.BorderColor[0] = 0;
		samplerDesc.BorderColor[1] = 0;
		samplerDesc.BorderColor[2] = 0;
		samplerDesc.BorderColor[3] = 0;
		samplerDesc.MinLOD = 0;
		samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

		// Create the texture sampler state.
		result = device->GetDevice()->CreateSamplerState(&samplerDesc, &mSampleState);
		CHECK(result);

		return true;
	}
	return false;
}

void TextureShader::DeinitializeShader()
{
	Shader::DeinitializeShader();
}

bool TextureShader::SetShaderParameters(D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, Texture* texture)
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	if(device)
	{
		Shader::SetShaderParameters(device, worldMatrix, viewMatrix, projectionMatrix);
	
		// Set shader texture resource in the pixel shader.
		ID3D11ShaderResourceView* d3dTexture = texture->GetResource();
		device->GetDeviceContext()->PSSetShaderResources(0, 1, &d3dTexture);
		return true;
	}
	return false;
}

bool TextureShader::SetShaderParameters(D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture)
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	if(device)
	{
		Shader::SetShaderParameters(device, worldMatrix, viewMatrix, projectionMatrix);
	
		// Set shader texture resource in the pixel shader.
		device->GetDeviceContext()->PSSetShaderResources(0, 1, &texture);
		return true;
	}
	return false;
}

bool TextureShader::RenderShader(D3DDevice* device, int vertextCount, int instanceCount)
{
	// Set the vertex input layout.
	device->GetDeviceContext()->IASetInputLayout(mLayout);

	// Set the vertex and pixel shaders that will be used to render this triangle.
	device->GetDeviceContext()->VSSetShader(mVertexShader, NULL, 0);
	device->GetDeviceContext()->PSSetShader(mPixelShader, NULL, 0);

	// Set the sampler state in the pixel shader.
	if(mSampleState)
	{
		device->GetDeviceContext()->PSSetSamplers(0, 1, &mSampleState);
	}

	device->GetDeviceContext()->DrawInstanced(vertextCount, instanceCount, 0, 0);
	return true;
}

#endif
#ifdef DX_9
TextureShader::TextureShader(void)
	:Shader("TextureShader","")
{
}

TextureShader::~TextureShader(void)
{
}
#endif